package com.lin.nio.channel.unblock;


import com.lin.nio.utils.ByteBufferUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * 非阻塞网络编程（同步非阻塞）
 *
 * * 非阻塞模式下，相关方法都不会让线程暂停
 *   * 在 ServerSocketChannel.accept 在没有连接建立时，会返回 null，继续运行
 *   * SocketChannel.read 在没有数据可读时，会返回 0，但线程不必阻塞，可以去执行其它 SocketChannel 的 read 或是去执行 ServerSocketChannel.accept
 *   * 写数据时，线程只是等待数据写入 Channel 即可，无需等 Channel 通过网络把数据发送出去
 * * 但非阻塞模式下，即使没有连接建立，和可读数据，线程仍然在不断运行，白白浪费了 cpu
 * * 数据复制过程中，线程实际还是阻塞的（AIO 改进的地方）
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
        // 使用 nio 来理解非阻塞模式, 单线程
        // 0. ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(16);

        // 1. 创建了服务器
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); // 设置通道的阻塞模式：非阻塞模式

        // 2. 绑定监听端口
        ssc.bind(new InetSocketAddress(1232));

        // 3. 连接集合
        List<SocketChannel> channels = new ArrayList<>();
        while (true) { // 线程会一直循环跑
//            System.out.println("服务端监听连接...");
            // 4. accept 建立与客户端连接， SocketChannel 用来与客户端之间通信
            SocketChannel sc = ssc.accept(); // 非阻塞，线程还会继续运行，如果没有连接建立，但sc是null
            if (sc != null) {
                System.out.println("服务端收到连接：" +  sc);
                sc.configureBlocking(false); // 设置和客户端的channel阻塞模式：非阻塞模式
                channels.add(sc);
            }
            for (SocketChannel channel : channels) {
                // 5. 接收客户端发送的数据
//                System.out.println("收到客户端消息前");
                int read = channel.read(buffer);// 非阻塞，线程仍然会继续运行，如果没有读到数据，read 返回 0
                if (read > 0) {
                    ByteBufferUtil.debugAll(buffer);
                    buffer.clear();
                    System.out.println("收到客户端消息后");
                }
            }
        }
    }
}
